/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.android.snake;

import java.util.ArrayList;
import java.util.Random;

import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

/**
 * SnakeView: implementation of a simple game of Snake
 * 
 * 
 */
public class SnakeView extends TileView {

    private static final String TAG = "SnakeView";

    /**
     * Current mode of application: READY to run, RUNNING, or you have already
     * lost. static final ints are used instead of an enum for performance
     * reasons.
     */
    private int mMode = READY;
    public static final int PAUSE = 0;
    public static final int READY = 1;
    public static final int RUNNING = 2;
    public static final int LOSE = 3;
    public static final int WIN = 4;  

    /**
     * Current direction the snake is headed.
     */
    private int mDirection1 = NORTH;
    private int mNextDirection1 = NORTH;
    private static final int NORTH = 1;
    private static final int SOUTH = 2;
    private static final int NORTHEAST = 3;
    private static final int NORTHWEST = 4;
    private static final int SOUTHEAST = 5;
    private static final int SOUTHWEST = 6;
    private int BOUNCE = 0;

    /**
     * Labels for the drawables that will be loaded into the TileView class
     */
    private static final int HOCKEY_PUCK = 1;
    private int T_COLOR=2;
    private int STEP = 20;

    /**
     * mScore: used to track the number of apples captured mMoveDelay: number of
     * milliseconds between snake movements. This will decrease as apples are
     * captured.
     */
    private long mScore = 0;
    private long mMoveDelay = 60;
    public int mTimer = 0;
    /**
     * mLastMove: tracks the absolute time when the snake last moved, and is used
     * to determine if a move should be made based on mMoveDelay.
     */
    private long mLastMove;
    
    /**
     * mStatusText: text shows to the user in some run states
     */
    private TextView mStatusText;

    /**
     * mPuck: a list of Coordinates that make up the snake's body
     * mAppleList: the secret location of the juicy apples the snake craves.
     */
    private ArrayList<Coordinate> mPuck = new ArrayList<Coordinate>();


    public int mBallon1_freq = 0;

    /**
     * Everyone needs a little randomness in their life
     */

    //Win conditions
    public int mBallon1_poped = 0;

    /**
     * Create a simple handler that we can use to cause animation to happen.  We
     * set ourselves as a target and we can use the sleep()
     * function to cause an update/invalidate to occur at a later date.
     */
    private RefreshHandler mRedrawHandler = new RefreshHandler();

    class RefreshHandler extends Handler {

        @Override
        public void handleMessage(Message msg) {
            SnakeView.this.update();
            SnakeView.this.invalidate();
        }

        public void sleep(long delayMillis) {
        	this.removeMessages(0);
            sendMessageDelayed(obtainMessage(0), delayMillis);
        }
    };


    /**
     * Constructs a SnakeView based on inflation from XML
     * 
     * @param context
     * @param attrs
     */
    public SnakeView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initSnakeView();
   }

    public SnakeView(Context context, AttributeSet attrs, int defStyle) {
    	super(context, attrs, defStyle);
    	initSnakeView();
    }

    private void initSnakeView() {
        setFocusable(true);
        Resources r = this.getContext().getResources();
        
        resetTiles(2);
        loadTile(HOCKEY_PUCK, r.getDrawable(R.drawable.hockeypuck));    	
    }
    

    private void initNewGame() {
        mPuck.clear();
        mPuck.add(new Coordinate(150, 250));
        mNextDirection1 = SOUTH;
        mMoveDelay = 60;
        mScore = 0;
    }


    /**
     * Given a ArrayList of coordinates, we need to flatten them into an array of
     * ints before we can stuff them into a map for flattening and storage.
     * 
     * @param cvec : a ArrayList of Coordinate objects
     * @return : a simple array containing the x/y values of the coordinates
     * as [x1,y1,x2,y2,x3,y3...]
     */
    private int[] coordArrayListToArray(ArrayList<Coordinate> cvec) {
        int count = cvec.size();
        int[] rawArray = new int[count * 2];
        for (int index = 0; index < count; index++) {
            Coordinate c = cvec.get(index);
            rawArray[2 * index] = c.x;
            rawArray[2 * index + 1] = c.y;
        }
        return rawArray;
    }

    /**
     * Save game state so that the user does not lose anything
     * if the game process is killed while we are in the 
     * background.
     * 
     * @return a Bundle with this view's state
     */
    public Bundle saveState() {
        Bundle map = new Bundle();

        map.putInt("mDirection1", Integer.valueOf(mDirection1));
        map.putInt("mNextDirection1", Integer.valueOf(mNextDirection1));
        map.putLong("mMoveDelay", Long.valueOf(mMoveDelay));
        map.putLong("mScore", Long.valueOf(mScore));
        map.putIntArray("mPuck", coordArrayListToArray(mPuck));

        return map;
    }

    /**
     * Given a flattened array of ordinate pairs, we reconstitute them into a
     * ArrayList of Coordinate objects
     * 
     * @param rawArray : [x1,y1,x2,y2,...]
     * @return a ArrayList of Coordinates
     */
    private ArrayList<Coordinate> coordArrayToArrayList(int[] rawArray) {
        ArrayList<Coordinate> coordArrayList = new ArrayList<Coordinate>();

        int coordCount = rawArray.length;
        for (int index = 0; index < coordCount; index += 2) {
            Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);
            coordArrayList.add(c);
        }
        return coordArrayList;
    }

    /**
     * Restore game state if our process is being relaunched
     * 
     * @param icicle a Bundle containing the game state
     */
    public void restoreState(Bundle icicle) {
        setMode(PAUSE);

        mDirection1 = icicle.getInt("mDirection1");
        mNextDirection1 = icicle.getInt("mNextDirection1");
        mMoveDelay = icicle.getLong("mMoveDelay");
        mScore = icicle.getLong("mScore");
        mPuck = coordArrayToArrayList(icicle.getIntArray("mPuck"));

    }

    /*
     * handles key events in the game. Update the direction our snake is traveling
     * based on the DPAD. Ignore events that would cause the snake to immediately
     * turn back on itself.
     * 
     * (non-Javadoc)
     * 
     * @see android.view.View#onKeyDown(int, android.os.KeyEvent)
     */
    @Override
    public boolean onTouchEvent(MotionEvent e){
    	if (mMode == WIN) return true;   	
    	float x=e.getX();
    	float y=e.getY();
    	Coordinate head1 = mPuck.get(0);
    	
    	if(e.getAction() == MotionEvent.ACTION_DOWN)           
    		changeDirection(checkProximity(head1, x, y)) ;
    		  		   	
    	return true;
    }
    
    public boolean checkWin(){
        Coordinate head1 = mPuck.get(0);        
        if(head1.y <= 10){
        	//Goal Width: (125 -> 175)
        	if ((head1.x > 125) && (head1.x < 175))  return true;
        }
        return false;     
    }
    
    public int checkProximity(Coordinate head1, float x,float y){
    	if(y >= head1.y && y <= head1.y+40){
    		if(x >= head1.x+10 &&  x <= head1.x+15) return 1;
    		if(x < head1.x+10 && x > head1.x) return 2;
    		if(x > head1.x+15 && x < head1.x+25) return 3;
    	}    		
    	return 0;
    }
    
    
    
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent msg) {

        if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
            if (mMode == READY | mMode == LOSE) {
                /*
                 * At the beginning of the game, or the end of a previous one,
                 * we should start a new game.
                 */
                initNewGame();
                setMode(RUNNING);
                update();
                return (true);
            }

            if (mMode == PAUSE) {
                setMode(RUNNING);
                update();
                return (true);
            }
            return (true);
        }

        

        return super.onKeyDown(keyCode, msg);
    }

    /**
     * Sets the TextView that will be used to give information (such as "Game
     * Over" to the user.
     * 
     * @param newView
     */
    public void setTextView(TextView newView) {
        mStatusText = newView;
    }

    /**
     * Updates the current mode of the application (RUNNING or PAUSED or the like)
     * as well as sets the visibility of textview for notification
     * 
     * @param newMode
     */
    public void setMode(int newMode) {
        int oldMode = mMode;
        mMode = newMode;

        if (newMode == RUNNING & oldMode != RUNNING) {
            mStatusText.setVisibility(View.INVISIBLE);
            update();
            return;
        }

        Resources res = getContext().getResources();
        CharSequence str = "";
        if (newMode == PAUSE) {
            str = res.getText(R.string.mode_pause);
        }
        if (newMode == READY) {
            str = res.getText(R.string.mode_ready);
        }
        if (newMode == LOSE) {
            str = res.getString(R.string.mode_lose_prefix) + mScore
                  + res.getString(R.string.mode_lose_suffix);
        }
        if (newMode == WIN) { 
        	str ="Game finished!! your score is:"+mScore;          
        }

        mStatusText.setText(str);
        mStatusText.setVisibility(View.VISIBLE);
    }

    public void update() {
        if (mMode == RUNNING) {
            long now = System.currentTimeMillis();
            CharSequence str = "";
            
            
    
            if(checkWin()){
            	mScore=1;
            	setMode(WIN);          	
            } else{
            	clearTiles();
            	updateSnake();            
	            mLastMove = now;            
	            mTimer = mTimer+1;
	            if (mTimer==40 || mTimer==30 || mTimer==20 || mTimer==10) str=(4-(mTimer/10))+" seconds left";
	            mStatusText.setText(str);
	            mStatusText.setVisibility(View.VISIBLE);
	            //if (mTimer==4000) setMode(LOSE);
            }
            
            mRedrawHandler.sleep(mMoveDelay);
        }

    }
    
    public void changeDirection(int direc){
        switch (direc) {
        case 0: return;
        case 1:{
        	if (mDirection1 == SOUTH) mNextDirection1=NORTH;
    		if (mDirection1 == NORTH) mNextDirection1=SOUTH;
    		BOUNCE = 10;
    		break;
    	}
        case 2:{
    		mNextDirection1=NORTHEAST;
    		BOUNCE = 5;
    		break;
    	}
        case 3:{
    		mNextDirection1=NORTHWEST;
    		BOUNCE = 5;
    		break;
    	}
        }
    }
   
    private void updateSnake() {

        // grab the snake by the head1
        Coordinate head1 = mPuck.get(0);
        Coordinate newHead1 = new Coordinate(60, 60);


        if ((head1.x < 5) || (head1.x > 295)){
	        switch (mDirection1) {
	        case NORTHEAST: {       	
        		mNextDirection1=NORTHWEST;
        		break;
        	}
	        case NORTHWEST: {       	
        		mNextDirection1=NORTHEAST;
        		break;
        	}
	        case SOUTHEAST: {       	
        		mNextDirection1=SOUTHWEST;
        		break;
        	}
	        case SOUTHWEST: {       	
        		mNextDirection1=SOUTHEAST;
        		break;
        	}
	        }
        	BOUNCE+=1;
        	
        	
        }
        
        if (head1.y < 10 && (head1.x<=100 || head1.x>=200)){

        	if (mDirection1 == NORTH) mNextDirection1=SOUTH;
        	if (mDirection1 == NORTHEAST) mNextDirection1=SOUTHEAST;        	
        	if (mDirection1 == NORTHWEST) mNextDirection1=SOUTHWEST;
        	BOUNCE+=1;	
        }
        
        mDirection1 = mNextDirection1;
        if(BOUNCE == 0){
        	newHead1 = new Coordinate(head1.x, head1.y);
        	mDirection1 = SOUTH;
        } else {
	        BOUNCE--;
	        switch (mDirection1) {
	        case NORTH: {
	            newHead1 = new Coordinate(head1.x, head1.y - STEP);
	            break;
	        }
	        case SOUTH: {
	            newHead1 = new Coordinate(head1.x, head1.y + STEP);
	            break;
	        }
	
	        case NORTHEAST: {
	            newHead1 = new Coordinate(head1.x+STEP, head1.y - STEP);
	            break;
	        }
	        case NORTHWEST: {
	            newHead1 = new Coordinate(head1.x-STEP, head1.y - STEP);
	            break;
	        }
	        case SOUTHEAST: {
	            newHead1 = new Coordinate(head1.x+STEP, head1.y + STEP);

	            break;
	        }
	        case SOUTHWEST: {
	            newHead1 = new Coordinate(head1.x-STEP, head1.y + STEP);

	            break;
	        }
	        }
        
        }

        // push a new head onto the ArrayList and pull off the tail
        mPuck.add(0, newHead1);
        mPuck.remove(mPuck.size()- 1);
        setTile(HOCKEY_PUCK, newHead1.x, newHead1.y);
    }

    private class Coordinate {
        public int x;
        public int y;

        public Coordinate(int newX, int newY) {
            x = newX;
            y = newY;
        }

        @Override
        public String toString() {
            return "Coordinate: [" + x + "," + y + "]";
        }
    }
    
}
